package chartCraft.tableDesk;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Observable;
import java.util.Observer;

import javax.swing.DefaultListSelectionModel;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.UIManager;

import javax.swing.border.Border;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import chartCraft.ChartCraftController;
import chartCraft.model.LocationModel;
import chartCraft.model.TopModel;
import chartCraft.model.Model.NotifyingMessage;

/***
 * @author ctldenon
 *
 */
public class ChartsTable extends JTable implements Observer{

	private static final long serialVersionUID = 1L;

	private ChartCraftController mChartCraftController;
	private TopModel mModel;




	public ChartsTable(ChartCraftController chartCraftController, TopModel topModel) {
		super(new ChartsTableModel(chartCraftController, topModel));
		mChartCraftController = chartCraftController;
		setSelectionModel(new ObserverSelectionModel(chartCraftController, topModel));

		JTableHeader header = getTableHeader();
		header.addMouseListener(new ColumnHeaderListener(chartCraftController, topModel));

		setModel(topModel);
	}

	public void addColumn(TableColumn aColumn) {
		TableCellRenderer renderer = new JComponentTableCellRenderer();
		aColumn.setHeaderRenderer(renderer);
		super.addColumn(aColumn);

	}

	class JComponentTableCellRenderer implements TableCellRenderer {
		public Component getTableCellRendererComponent(JTable table, Object value,
				boolean isSelected, boolean hasFocus, int row, int column) {

			JLabel result = null;

			if (column == 0) {
				result = new JLabel();
			} else {
				String string = mModel.getNameForSeries(column -1);
				Color color = mModel.getColorForSeries(column -1);
				result = new JLabel(string, new ColorIcon(color), JLabel.CENTER);
				

			}
			Border headerBorder = UIManager.getBorder("TableHeader.cellBorder");
			result.setBorder(headerBorder);
			return result;
		}
	}



	public void setModel(TopModel mTopModel) {
		((ChartsTableModel) this.getModel()).setModel(mTopModel);
		mModel = mTopModel;
		mModel.addObserver(this);
		
		getTableHeader().repaint();
	}

	@Override
	public void update(Observable o, Object arg) {
		if (o == mModel) {
			NotifyingMessage msg = (NotifyingMessage) arg;
			String action = msg.getAction();

			if (action == TopModel.SERIES_COLOR_CHANGED) {
				getTableHeader().repaint();

			} else if (action == TopModel.SERIES_NAME_CHANGED) {
				getTableHeader().repaint();
			}
		}
	}
}

class ObserverSelectionModel extends DefaultListSelectionModel implements Observer{

	private static final long serialVersionUID = 1L;

	ChartCraftController mChartCraftController;
	TopModel mModel;

	public ObserverSelectionModel(ChartCraftController chartCraftController, TopModel topModel){
		mChartCraftController = chartCraftController;
		setModel(topModel);
		setSelectionMode(MULTIPLE_INTERVAL_SELECTION);
	}

	public void setModel(TopModel model) {
		mModel = model;
		mModel.addObserver(this);
		
		reloadFromState();
	}

	public boolean isSelectedIndex(int index) {
		if (index >= 0 && index < mModel.getNoOfLocations()) {
			final LocationModel locationModel = mModel.getChildLocationModel(index);
			if (locationModel != null) {
				return locationModel.isSelect();
			}
		} 
		return false;
	}

	@Override
	public void setSelectionInterval(int startIndex, int endIndex) {
		mChartCraftController.setSelectionOnly(startIndex, endIndex + 1);
	}

	@Override
	public void removeSelectionInterval(int startIndex, int endIndex) {
		mChartCraftController.changeSelectionTo(startIndex, endIndex + 1, false);
	}

	@Override
	public void addSelectionInterval(int startIndex, int endIndex)  {
		mChartCraftController.changeSelectionTo(startIndex, endIndex + 1, true);
	}

	public void clearSelection() {
		mChartCraftController.deselectAllLocations();
	}


	@Override
	public void update(Observable o, Object arg) {
		if (o == mModel) {
			NotifyingMessage msg = (NotifyingMessage) arg;
			String action = msg.getAction();

			if (action == TopModel.INSERT_NEW_LOCATION_MODEL) {
				LocationModel locationModel = (LocationModel) msg.getObject();
				locationModel.addObserver(this);

			} else if (action == TopModel.DELETE_LOCATION_MODEL) {
				LocationModel locationModel = (LocationModel) msg.getObject();
				locationModel.deleteObserver(this);

			}


		} else if (o instanceof LocationModel) {

			NotifyingMessage msg = (NotifyingMessage) arg;
			LocationModel locationModel = (LocationModel) o;
			String action = msg.getAction();

			if (action == LocationModel.SELECTION_STATUS) {
				int locationId = mModel.getLocationId(locationModel);
				fireValueChanged(locationId, locationId, true);
			} 
		}		
	}
	
	public void reloadFromState() {
		for (LocationModel locationModel: mModel.getAllLocationModels()) {
			locationModel.addObserver(this);
		}
	}
}

class ChartsTableModel extends AbstractTableModel implements Observer{

	private static final long serialVersionUID = 1L;

	ChartCraftController mChartCraftController;
	TopModel mModel;

	public ChartsTableModel(ChartCraftController chartCraftController, TopModel topModel){
		mChartCraftController = chartCraftController;
		setModel(topModel);
	}

	public void setModel(TopModel model) {
		mModel = model;
		mModel.addObserver(this);
		
		reloadFromState();
	}

	public int getRowCount() { 
		return  mModel.getNoOfLocations(); 
	}

	public int getColumnCount() { 
		return mModel.getNoOfSeries() + 1; //the extra column for row name
	}

	public Object getValueAt(int row, int col) {
		int locationOffset = row;
		if (col == 0) {
			return mModel.getChildLocationModel(locationOffset).getName();
		} 
		int seriesOffset = col - 1; //excluding the row name
		int currentTimeId = mModel.getCurrentTimeId();
		return mModel.getChildLocationModel(locationOffset).getValueAt(currentTimeId, seriesOffset);
	}

	public boolean isCellEditable(int row, int col) { 
		return true; 
	}

	public void setValueAt(Object value, int row, int col) {
		int locationOffset = row;
		if (col == 0) {
			//location name
			mChartCraftController.setLocationName(locationOffset, (String) value);
		} else {
			int seriesOffset = col - 1;
			int currentTimeId = mModel.getCurrentTimeId();
			mChartCraftController.setValueOfCurrentTimeAt(locationOffset, seriesOffset, currentTimeId, value);
		}
	}

	@Override
	public void update(Observable o, Object arg) {
		if (o == mModel) {
			NotifyingMessage msg = (NotifyingMessage) arg;
			String action = msg.getAction();

			if (action == TopModel.INSERT_NEW_LOCATION_MODEL) {
				fireTableStructureChanged();
				LocationModel locationModel = (LocationModel) msg.getObject();
				locationModel.addObserver(this);

			} else if (action == TopModel.DELETE_LOCATION_MODEL) {
				fireTableStructureChanged();
				LocationModel locationModel = (LocationModel) msg.getObject();
				locationModel.deleteObserver(this);
			} else if (action == TopModel.CURRENT_TIME_UPDATED) {
				fireTableDataChanged();

			}


		} else if (o instanceof LocationModel) {

			NotifyingMessage msg = (NotifyingMessage) arg;
			String action = msg.getAction();

			if (action == LocationModel.DATA_CHANGE) {
				fireTableDataChanged();

			} else if (action == LocationModel.NAME_CHANGE) {
				fireTableDataChanged();

			} else if (action == LocationModel.STUCTURE_CHANGE) {
				fireTableStructureChanged();

			}

		}
	}
	

	private void reloadFromState() {
		for (LocationModel locationModel: mModel.getAllLocationModels()) {
			locationModel.addObserver(this);
		}
		
		fireTableStructureChanged();
		fireTableDataChanged();
	}
}

class ColumnHeaderListener extends MouseAdapter {
	private EditColumnHeadDialog editColumnHeadDialog;


	public ColumnHeaderListener(ChartCraftController chartCraftController, TopModel model) {
		editColumnHeadDialog = new EditColumnHeadDialog(chartCraftController, model);
	}
	/*
	 * Adapted from http://www.exampledepot.com/egs/javax.swing.table/ColHeadEvent.html
	 */
	public void mouseClicked(MouseEvent evt) {
		JTable table = ((JTableHeader) evt.getSource()).getTable();
		TableColumnModel colModel = table.getColumnModel();

		// The index of the column whose header was clicked
		int viewColIndex = colModel.getColumnIndexAtX(evt.getX());
		int modelColIndex = table.convertColumnIndexToModel(viewColIndex);

		if (viewColIndex > 0){
			editColumnHeadDialog.show(modelColIndex-1);
			return;
		}

	}
}

class ColorIcon extends ImageIcon {
	public static final int WIDTH = 10;
	public static final int HEIGHT = 10;
	
	private Color mColor;
	
	public ColorIcon(Color color) {
		super();
		mColor = color;
	}
	
	public void paintIcon(Component c, Graphics g, int x, int y) {

		g.setColor(mColor);
		g.fillRect(x, y, WIDTH ,HEIGHT);
		g.setColor(Color.BLACK);
		g.drawRect(x, y, WIDTH ,HEIGHT);

	}
	
	public int getIconWidth() {
        return WIDTH;
    }
    
    public int getIconHeight() {
        return HEIGHT;
    }
}


